import json
from datetime import timedelta

from django.utils import timezone

from utils.snowflake import generate_id    # 导入雪花算法生成订单号
from django.db import transaction
from django.shortcuts import render
from orders.models import Order,OrderDetail
from list.models import Course,CourseExpire
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from django_redis import get_redis_connection
import logging
logging = logging.getLogger('django')
from rest_framework.response import Response
# Create your views here.


class OrderCreateView(APIView):

    # 同样要验证登陆
    permission_classes = []
    def post(self,request):
        # 当你点击提交订单结算的时候，需要获取该用户的购物车数据，并生成相应数量的订单详情和订单大表
        # 并进行生成订单，生成订单后才能生成二维码
        # 订单生成后，需要将购物车数据进行删除！！！一定要记得
        # 但当你订单生成后自动删除购物车的数据就会导致订单页面访问无数据，所以
        # 我们需要在订单生成后，判断如果当前购物车以晴空就返回首页继续选购，不能一致停留在空页面
        if not request.user.is_authenticated:
            logging.error('用户未登陆')
            return Response({
                'code':2,
                'message':'用户未登陆'
            })

        # 获取购物车参数:传递这些参数主要是为了写入(生成订单)
        # 我们要获取，购物中的课程id，价格(和后端对比)，总价格(和后端对比),支付方式
        # 是否使用优惠券、优惠券id

        # 判断参数
        course_list = request.data.get('course_list')
        use_coupon_id = request.data.get('use_coupon_id')
        use_coupon = request.data.get('use_coupon')
        pay_method = request.data.get('pay_method')
        total_price = request.data.get('total_price')
        real_price = request.data.get('real_price')
        if not course_list:
            logging.error('购物车为空')
            return Response({
                'code':0,
                'message':'购物车为空'
            })


        # 先获取购物车的数据-->切记在redis中呢
        redis_conn = get_redis_connection('default')
        # 定义user_id的key
        user_id = request.user.id
        carts_key = f'carts:view:shopping:{user_id}'

        # 遍历传过来的购物车结算课程参数 获取参数的课程id 组成新列表
        course_ids = [courseId['course_id'] for courseId in course_list]
        logging.info(f"前端结算传递的课程ID列表:{course_ids}")

        # 遍历这个课程列表
        # 开始判断你前段传过来的这些课程 是否在你redis购物车数据中
        for course_id in course_ids:
            # 找到我们之前的购物车小key：course_id
            course_key = f"course:{course_id}"
            cart_item = redis_conn.hget(carts_key,course_key)
            if not cart_item:
                logging.error(f"购物车中没有该课程:{course_id}")
                return Response({
                    'code':0,
                    'message':f"购物车中没有该课程:{course_id}"
                })

            # 找到的话开始对他解码，因为要判断这些药结算的课程状态是否是选中，选中才能结算，
            # 如果有没有选中的，提醒还有未选中的
            # 因为你要结算这些课程嘛，所以他的状态必须是选中吗不选怎么结算呢，金额也肯定不对的
            cart_item_decode = json.loads(cart_item.decode())
            # 因为现在是在循环地喜爱，可以直接取值
            if cart_item_decode['is_selected'] == 0:
                logging.error(f"课程:{course_id}未选中")
                return Response({
                    'code':0,
                    'message':f"课程:{course_id}未选中"
                })

        # 校验课程的状态
        # 判断购物车中的课程的状态是否为上架和为被删除
        # 这里不再循环遍历去判断了，直接判断结算的课程是否存在数据库中
        # 如果存在判断他的上架状态
        courses = Course.objects.filter(id__in=course_ids,status=1)

        # 以上是符合验证的课程 如果上面的上架的课程和购物车中要结算的课程数量一致，那就说明可以结算，没有问题
        if len(courses) != len(course_list):
            logging.error('有课程未上架')
            return Response({
                'code':0,
                'message':'有课程未上架'
            })


        # 开始校验：价格和总价格
        # 首先定义总价格-->为0，因为我们要搞一个空的后面去进行手动计算并和传过来的进行对比
        calculate_total = 0

        # 遍历购物车中的课程，计算总价格
        for item in course_list:
            # 能通过这里的课程对象，取到他的有效期信息
            course_id = item['course_id']
            expire_time = item['expire_time']
            price = item['price']

            if expire_time == 0:  # 永久有效，别忘了你的过期表里这个字段是int类型：0,30,60,90
                # 永久有效就从原课程表里的价格取
                course_price = Course.objects.get(id=course_id).price  # 直接去他的价格
                logging.info(f"从数据库中课程表获取的课程[{course_id}]价格:[{course_price}]")
            else:  # 有不同有效期的，也就是不为0的
                course_price = CourseExpire.objects.get(course_id=course_id,expire_time=expire_time).price
                logging.info(f"从数据库中课程有效期表获取的课程[{course_id}]价格:[{course_price}]")
                # 这个course_price是重新赋值的

            # 进行和前端传的价格进行对比
            if float(course_price) != float(price):  # 以防万一 转换类型
                logging.info(f"前端传递的价格:[{price}]和数据库中获取的价格:[{course_price}]")
                return Response({
                    'code':0,
                    'message':'价格有误'
                })

            # 计算总价格
            calculate_total += course_price
            logging.info(f"计算后的总价格:[{calculate_total}]")



        # 这里注意缩进 是已经结束判断单个的了
        # 开始对比
        if float(calculate_total) != float(total_price):
            logging.info(f"前端传递的总价格:[{total_price}]和计算后的总价格:[{calculate_total}]不相符")
            return Response({
                'code':0,
                'message':'价格有误'
            })


        # 如果以上条件都合格，开始写入订单表和订单详情表：就是进行create创建
        try:
            with transaction.atomic():
            # 这段代码的作用是开启一个数据库事务，确保在 with 块中的所有数据库操作要么全部成功，要么全部失败回滚。
            # 使用 transaction.atomic() 可以保证数据一致性
            # 若块内发生异常，事务会自动回滚
            # 若执行成功，则事务会被提交
            # 要么全部创建表成功，要么全部创建表失败，详情表与订单表同生共死
                order = Order.objects.create(
                    pay_method=pay_method,
                    use_coupon=bool(use_coupon),
                    use_coupon_id=use_coupon_id,
                    total_price=total_price,
                    real_price=real_price,
                    order_status=0,  # 都还没付钱呢
                    order_desc=f"码境空间:{str(generate_id())}",
                    user = request.user,
                    # 订单号生成使用雪花算法
                    order_number=str(generate_id())
                    # 订单时间、下单时间：自动生成
                )



                # 创建订单详情表
                for item in course_list:
                    # 因为购物车选中结算了几个商品就有几个详情单，所以遍历进行创建
                    # 能通过这里的课程对象，取到他的有效期信息
                    course_id = item['course_id']
                    expire_days = item['expire_time']
                    expire_text = item['expire_text']
                    price = item['price']
                    expire_time = None
                    # 先设置为0，因为过期时间是需要算的，
                    # 如果你是永久有效，也就是过期时间等于0，那你的时间就不用算
                    # 如果你是30,60,90，那就要下单时间+顺眼的30,60,90，用到顺延方法
                    if expire_days != 0:  # 永久有效
                        expire_time = timezone.now() + timedelta(days=expire_days)

                    # 条件就绪可以创建了
                    OrderDetail.objects.create(
                        order=order,  # 上面创建的大表
                        course = Course.objects.get(id=course_id),
                        price=price,
                        expire_days=expire_days,
                        expire_text=expire_text,
                        expire_time=expire_time,
                        order_number=order.order_number   # 上面有了 这个就能通过外键拿到了，因为两个是一致的
                    )



                # 以上是你点击了结算-->验证无误-->生成订单；
                # 但生成后购物车中的数据都要进行删除，因为已经是购买的商品了
                # 这里一定还要记得，购物车数据一提到就是操作redis的数据
                for course_id in course_ids:
                    # 找到我们之前的购物车小key：course_id
                    course_key = f"course:{course_id}"
                    redis_conn.hdel(carts_key,course_key)
                    return Response({
                        'code':1,
                        'message':'订单创建成功',
                        'data':{   # 返回订单信息，总价格 实付价格 以及 唯一订单号
                            'real_price':order.real_price,
                            'total_price':order.total_price,
                            'order_number':order.order_number
                        }
                    })

        except Exception as e:
            logging.error(f"订单创建失败:错误信息为{e}")
            return Response({
                'code': 0,
                'errmsg': f"订单创建失败:错误信息为{e}"
            })






























